package com.wjd.wshow.controller;

import com.wjd.wshow.bean.VideoSearchBean;
import com.wjd.wshow.common.resource.Resource;
import com.wjd.wshow.common.resource.ResourceType;
import com.wjd.wshow.common.resource.loader.ResourceLoader;
import com.wjd.wshow.common.resource.loader.Uploader;
import com.wjd.wshow.common.resource.pojo.UploadResource;
import com.wjd.wshow.entity.Video;
import com.wjd.wshow.service.VideoService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

@ResponseBody
@RequestMapping("/videos")
@RestController("VideoController")
public class VideoController {

    private static Logger logger = LoggerFactory.getLogger(VideoController.class);

    /**
     * 返回块大小，10M
     */
    private static int blockSize = 1 * 1024 * 1024;

    @javax.annotation.Resource(name = "VideoService")
    private VideoService videoService;

    @GetMapping("/list_all")
    public List<Video> getListVideos() {
        return videoService.getVideos();
    }

    @PostMapping("/list")
    public List<Video> listVideos(@RequestBody VideoSearchBean bean) {
        if (bean.getPath() == null) {
            throw new IllegalArgumentException();
        }
        return videoService.findVideos(bean.getPath());
    }

    @PostMapping("/upload")
    public final String uploadVideo(@RequestParam(name = "file") MultipartFile file,
                                    @RequestParam(name = "path", defaultValue = "") String path) {
        if (file == null || file.isEmpty()) {
            return null;
        }

        try {
            // 上传文件信息
            String contentType = file.getContentType();
            String filename = file.getOriginalFilename();
            Path filePath = Paths.get(path, filename);

            // 上传资源
            UploadResource uploadResource = new UploadResource();
            uploadResource.setType(ResourceType.VIDEO);
            uploadResource.setPath(filePath);
            uploadResource.setFilename(filename);
            uploadResource.setInputStream(file.getInputStream());
            uploadResource.setSize(file.getSize());

            // 上传
            new Uploader().upload(uploadResource);
            Path uploadPath = uploadResource.getPath();
            logger.info("upload file path: " + uploadPath);

            // 保存数据库
            Video video = new Video();
            video.setType(contentType);
            video.setName(filename);
            video.setPath(uploadPath.toString());
            videoService.addVideo(video);
            return video.getId();
        } catch (IOException e) {
            e.printStackTrace();
            logger.error(e.getCause().getMessage());
        }

        return null;
    }

    /**
     * 获取视频数据
     * @param videoId 视频id
     * @param range 数据范围
     * @param response 响应对象
     */
    @GetMapping("/{videoId}")
    public final void randomRetrieveVideo(@PathVariable String videoId,
                                          @RequestHeader(value = "Range", required = false) String range,
                                          HttpServletResponse response) {
        response.setCharacterEncoding("utf-8");
        try {
            // 获取视频资源
            Resource resource = videoService.getResource(videoId);
            if (resource == null || resource.getPath() == null) {
                response.setStatus(HttpServletResponse.SC_NOT_FOUND);
                return;
            }

            // 资源属性
            long length = resource.getSize();
            String resName = resource.getPath().toFile().getName();

            // 数据请求范围解析
            Long[] ranges = parseRangeHeader(range);
            if (ranges != null && (ranges.length == 0 || ranges.length % 2 != 0)) {
                response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
                return;
            }

            // 数据范围以及长度
            long rangeStart = 0;
            long rangeEnd = length - 1;
            if (ranges != null) {
                if (ranges[0] != null) {
                    rangeStart = ranges[0];
                }
                if (ranges[1] != null) {
                    rangeEnd = ranges[1];
                }
            }
            long contentLength = rangeEnd - rangeStart + 1;
            if (contentLength > blockSize) {
                contentLength = blockSize;
                rangeEnd = rangeStart + contentLength - 1;
            }
            boolean partial = rangeStart != 0 || rangeEnd != length - 1;

            // 响应头部
            response.setHeader("Content-disposition", "attachment; filename=" + resName);
            response.setHeader("Content-Type", "application/octet-stream");
            response.setHeader("Accept-Ranges", "bytes");
            response.setHeader("Cache-Control", String.format("%s=%d", "max-age", 60 * 60));
            response.setHeader("Connection", "keep-alive");
            response.setHeader("Content-Transfer-Encoding", "binary");
            response.setHeader("Content-Length", String.format("%d", contentLength));
            response.setHeader("E-Tag", String.format("%s(%d-%d)", videoId, rangeStart, rangeEnd));
            if (partial) {
                response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
                String contentRange = String.format("bytes %d-%d/%d", rangeStart, rangeEnd, length);
                response.setHeader("Content-Range", contentRange);
                logger.info("Video Partial Content Range: " + contentRange);
            }

            // 输出数据流
            OutputStream output = response.getOutputStream();
            ResourceLoader.getLoader().randomCopyResource(resource, output, rangeStart, contentLength);
        } catch (IOException e) {
            logger.error(e.getCause().getMessage());
        }
    }

    /**
     * 解析范围字符串
     * @param range http range
     * @return 范围数组 [起始值, 终止值, ...]
     */
    private Long[] parseRangeHeader(String range) {
        if (range == null) {
            return null;
        }
        Long[] arr;
        List<Long> rangeList = new ArrayList<>();
        range = range.replace("bytes=","");
        String[] ranges = range.split(",");
        for (String r : ranges) {
            String[] rs = r.split("-");
            Long start = null;
            Long end = null;
            if (rs.length > 0) {
                start = Long.parseLong(rs[0]);
            }
            if (rs.length > 1) {
                end = Long.parseLong(rs[1]);
            }
            rangeList.add(start);
            rangeList.add(end);
        }
        arr = rangeList.toArray(new Long[0]);
        return arr;
    }

}
